package org.easymodelers.gmf.creator.impl;

import java.io.IOException;

import org.easymodelers.gmf.creator.GmfMapCreator;
import org.easymodelers.gmf.utils.GmfModelsUtils;
import org.easymodelers.gmf.utils.ModelCreatorCallback;
import org.easymodelers.modeler.data.Element;
import org.easymodelers.modeler.data.ElementLabel;
import org.easymodelers.modeler.data.Modeler;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.gmf.gmfgraph.Canvas;
import org.eclipse.gmf.mappings.CanvasMapping;
import org.eclipse.gmf.mappings.FeatureLabelMapping;
import org.eclipse.gmf.mappings.GMFMapFactory;
import org.eclipse.gmf.mappings.LabelTextAccessMethod;
import org.eclipse.gmf.mappings.Mapping;
import org.eclipse.gmf.mappings.NodeMapping;
import org.eclipse.gmf.mappings.TopNodeReference;
import org.eclipse.gmf.tooldef.ToolRegistry;

public class DefaultGmfMapCreator extends AbstractPreModelsCreator implements GmfMapCreator {

	private GMFMapFactory getMapFactory() {
		return GMFMapFactory.eINSTANCE;
	}
	
	public void createGmfMap(final String modelPath, final Modeler modeler)	throws IOException {
		final Canvas canvas = getGmfGraphModelFromGmfMapModel(modelPath);
		final ToolRegistry toolRegistry = getGmfToolModelFromGmfMapModel(modelPath);
		final EPackage metaModel = getMetaModelFromGmfMapModel(modelPath);
		
		GmfModelsUtils.createGmfModel(modelPath, new ModelCreatorCallback<Mapping>() {
			public Mapping createModelRootElement() throws IOException {
				GMFMapFactory mapFactory = getMapFactory();
				Mapping mapping = mapFactory.createMapping();
				ModelsManager modelsManager = new ModelsManager(canvas, toolRegistry, mapping, metaModel);
				return createRootElement(mapFactory, modelsManager, modeler);
			}
		});
	}

	private Mapping createRootElement(GMFMapFactory mapFactory,
						ModelsManager modelsManager, Modeler modeler) {
		CanvasMapping canvasMapping = mapFactory.createCanvasMapping();
		
		canvasMapping.setDiagramCanvas(modelsManager.getCanvas());
		canvasMapping.setPalette(modelsManager.getPalette());
		canvasMapping.setDomainModel(modelsManager.getMetaModel());
		canvasMapping.setDomainMetaElement(modelsManager.getMetaModelEClass(
														modeler.getBinding()));
		
		Mapping mapping = modelsManager.getMapping();
		mapping.setDiagram(canvasMapping);
		
		for (Element element : modeler.getElements()) {
			handleElement(element, mapping, modelsManager);
		}
		
		return mapping;
	}
	
	private void handleElement(Element element, Mapping mapping, ModelsManager modelsManager) {
		GMFMapFactory mapFactory = getMapFactory();
		
		TopNodeReference topNodeReference = mapFactory.createTopNodeReference();

		EReference attributeRef = modelsManager.getMetaModelEReference(
									element.getContainerBindingClassName(),
									element.getContainerBindingReferenceName());
		topNodeReference.setChildrenFeature(attributeRef);
		topNodeReference.setContainmentFeature(attributeRef);
		
		NodeMapping nodeMapping = mapFactory.createNodeMapping();
		nodeMapping.setDomainMetaElement(
				modelsManager.getMetaModelEClass(element.getBinding()));
		nodeMapping.setDiagramNode(modelsManager.getNodeForElement(element));
		nodeMapping.setTool(modelsManager.getToolForElement(element));
		topNodeReference.setOwnedChild(nodeMapping);
		
		ElementLabel elementLabel = element.getLabel();
		FeatureLabelMapping labelMapping = mapFactory.createFeatureLabelMapping();
		labelMapping.setEditMethod(LabelTextAccessMethod.MESSAGE_FORMAT);
		labelMapping.setViewMethod(LabelTextAccessMethod.MESSAGE_FORMAT);
		labelMapping.setDiagramLabel(modelsManager.getDiagramLabelForLabel(elementLabel));
		EAttribute labelAttribute = modelsManager.getMetaModelEAttribute(
						elementLabel.getBindingClassName(), elementLabel.getBindingAttributeName());
		labelMapping.getFeatures().add(labelAttribute);
		labelMapping.getEditableFeatures().add(labelAttribute);
		nodeMapping.getLabelMappings().add(labelMapping);
		
		mapping.getNodes().add(topNodeReference);
	}

}
